package com.edev.support.utils;

import com.edev.support.exception.OrmException;
import lombok.NonNull;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * Java Bean反射操作工具类（静态方法）
 * 
 * 核心功能：
 * 1. 类加载与实例化
 * 2. 字段值读写（支持私有字段）
 * 3. 方法查找与反射调用
 * 
 * 典型使用场景：
 * - ORM框架中的实体对象动态创建
 * - AOP代理对象的方法反射调用
 * - 动态配置注入到私有字段
 * 
 * 注意事项：
 * - 所有方法均为静态方法，不可实例化
 * - 字段操作会临时修改访问权限（完成后自动恢复）
 */
public class BeanUtils {
    private BeanUtils() {}

    /**
     * 根据全限定类名加载Class对象
     * @param className 完整类名（如：com.example.User）
     * @return 对应的Class对象
     * @throws OrmException 类不存在时抛出
     */
    public static Class<?> getClazz(@NonNull String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new OrmException("The Class is not Found:[%s]", className);
        }
    }

    /**
     * 通过反射创建对象实例（需无参构造函数）
     * @param clazz 目标类Class对象
     * @return 新创建的实例
     * @param <T> 目标类型
     * @throws OrmException 出现以下情况时抛出：
     *                      - 类无法实例化（抽象类/接口）
     *                      - 无默认构造函数
     *                      - 访问权限不足
     */
    public static <T> T createBean(@NonNull Class<T> clazz) {
        try {
            return clazz.newInstance();
        } catch (InstantiationException e) {
            throw new OrmException("Instance the entity error:[%s]", e, clazz);
        } catch (IllegalAccessException e) {
            throw new OrmException("Illegal access the entity:[%s]", e, clazz);
        }
    }

    /**
     * 设置对象字段值（支持私有字段）
     * @param obj 目标对象
     * @param fieldName 字段名称（区分大小写）
     * @param value 要设置的值
     * @throws OrmException 字段不存在或访问非法时抛出
     */
    public static void setValue(@NonNull Object obj, @NonNull String fieldName, Object value) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            boolean isAccessible = field.isAccessible();
            if(!isAccessible) field.setAccessible(true);
            field.set(obj, value);
            field.setAccessible(isAccessible);
        } catch (IllegalAccessException e) {
            throw new OrmException("Illegal access the field[%s] in the entity[%s]", e, fieldName, obj);
        } catch (NoSuchFieldException e) {
            throw new OrmException("No such field:[object: %s, field: %s]", obj, fieldName);
        }
    }

    /**
     * 获取对象字段值（支持私有字段）
     * @param obj 目标对象
     * @param fieldName 字段名称（区分大小写）
     * @return 字段当前值
     * @throws OrmException 字段不存在或访问非法时抛出
     */
    public static Object getValue(@NonNull Object obj, @NonNull String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            boolean isAccessible = field.isAccessible();
            if(!isAccessible) field.setAccessible(true);
            Object value =  field.get(obj);
            field.setAccessible(isAccessible);
            return value;
        } catch (IllegalAccessException e) {
            throw new OrmException("Illegal access the field[%s] in the object[%s]", e, fieldName, obj);
        } catch (NoSuchFieldException e) {
            throw new OrmException("No such field:[object: %s, field: %s]", obj, fieldName);
        }
    }

    /**
     * 根据方法名和参数数量查找方法（支持重载方法）
     * @param obj 目标对象
     * @param methodName 方法名称（区分大小写）
     * @param sizeOfParameters 参数数量
     * @return 匹配的方法对象，未找到返回null
     */
    public static Method getMethod(@NonNull Object obj, @NonNull String methodName, int sizeOfParameters) {
        Method target = null;
        for(Method method : obj.getClass().getMethods())
            if(method.getName().equals(methodName) &&
                method.getParameterTypes().length==sizeOfParameters)
                    target = method;
        return target;
    }

    /**
     * 根据方法名查找方法(不建议使用在重载方法中)
     * <p>
     * 特别说明：如果有多个同名的方法，返回最后一个匹配的声明方法
     * @param service 目标对象
     * @param methodName 方法名称（区分大小写）
     * @return 匹配的方法对象
     * @throws OrmException 方法不存在时抛出
     */
    public static Method getMethod(@NonNull Object service, @NonNull String methodName) {
        Method[] allOfMethods = service.getClass().getDeclaredMethods();
        Method rtn = null;
        for(Method method : allOfMethods) {
            if(method.getName().equals(methodName)) rtn = method;
        }
        if(rtn!=null) return rtn; //if it has an override, return the last one.
        throw new OrmException("No such method[%s] in the service[%s]", methodName, service.getClass().getName());
    }

    /**
     * 反射调用对象方法
     * @param service 目标对象
     * @param method 要调用的方法对象
     * @param args 方法参数（需严格匹配参数类型）
     * @return 方法执行结果
     * @throws OrmException 调用失败时抛出（参数不匹配/访问权限不足等）
     */
    public static Object invoke(@NonNull Object service, @NonNull Method method, Object...args) {
        try {
            if(args==null||args.length==0) return method.invoke(service);
            else return method.invoke(service, args);
        } catch (IllegalAccessException | IllegalArgumentException
                | InvocationTargetException e) {
            throw new OrmException("error when invoking the service by reflect [method: %s, args: %s]",
                    e, method, Arrays.toString(args));
        }
    }
}
